home *** CD-ROM | disk | FTP | other *** search
/ Tech Arsenal 1 / Tech Arsenal (Arsenal Computer).ISO / tek-01 / dmake38c.zip / GETCWD.C < prev    next >
C/C++ Source or Header  |  1992-01-23  |  6KB  |  232 lines

  1. /*
  2.     getcwd -- get pathname of current working directory
  3.  
  4.     public-domain implementation
  5.  
  6.     last edit:    03-Nov-1990    Gwyn@BRL.MIL
  7.  
  8.     complies with the following standards:
  9.         IEEE Std 1003.1-1988
  10.         SVID Issue 3
  11.         X/Open Portability Guide Issue 2 (when "XPG2" is defined)
  12.         X/Open Portability Guide Issue 3
  13.  
  14.     This implementation of getcwd() can be used to replace the UNIX
  15.     System V library routine (which uses popen() to capture the output of
  16.     the "pwd" command).  Once that is done, "pwd" can be reimplemented as
  17.     just puts(getcwd((char*)0,0)), assuming "XPG2" is defined below.
  18.  
  19.     This implementation depends on every directory having entries for
  20.     "." and "..".  It also depends on the internals of the <dirent.h>
  21.     data structures to some degree.
  22.  
  23.     I considered using chdir() to ascend the hierarchy, followed by a
  24.     final chdir() to the path being returned by getcwd() to restore the
  25.     location, but decided that error recovery was too difficult that way.
  26.     The algorithm I settled on was inspired by my rewrite of the "pwd"
  27.     utility, combined with the dotdots[] array trick from the SVR2 shell.
  28. */
  29. #define XPG2    /* define to support obsolete XPG2-mandated feature */
  30.  
  31.  
  32. #include    <sys/types.h>
  33. #include    <sys/stat.h>
  34.  
  35. #ifdef M_XENIX
  36. # include        <sys/ndir.h>
  37. # define dirent direct
  38. #else
  39. # include        <dirent.h>
  40. #endif
  41.  
  42. #include    <errno.h>
  43. #include    <string.h>
  44.  
  45. typedef char    *pointer;        /* (void *) if you have it */
  46.  
  47. extern void    free();
  48. extern pointer    malloc();
  49. extern int    fstat(), stat();
  50.  
  51. extern int    errno;            /* normally done by <errno.h> */
  52.  
  53. #ifndef NULL
  54. #define    NULL    0            /* amorphous null pointer constant */
  55. #endif
  56.  
  57. #ifndef NAME_MAX
  58. #define    NAME_MAX    255        /* maximum directory entry size */
  59. #endif
  60.  
  61.  
  62. char    *
  63. getcwd( buf, size )            /* returns pointer to CWD pathname */
  64.     char        *buf;        /* where to put name (NULL to malloc) */
  65.     int        size;        /* size of buf[] or malloc()ed memory */
  66.     {
  67.     static char    dotdots[] =
  68. "../../../../../../../../../../../../../../../../../../../../../../../../../..";
  69.     char        *dotdot;    /* -> dotdots[.], right to left */
  70.     DIR        *dirp;        /* -> parent directory stream */
  71.     struct dirent    *dir;        /* -> directory entry */
  72.     struct stat    stat1,
  73.             stat2;        /* info from stat() */
  74.     struct stat    *d = &stat1;    /* -> info about "." */
  75.     struct stat    *dd = &stat2;    /* -> info about ".." */
  76.     register char    *buffer;    /* local copy of buf, or malloc()ed */
  77.     char        *bufend;    /* -> buffer[size] */
  78.     register char    *endp;        /* -> end of reversed string */
  79.     register char    *dname;        /* entry name ("" for root) */
  80.     int        serrno = errno;    /* save entry errno */
  81.  
  82.     if ( buf != NULL && size <= 0
  83. #ifndef XPG2
  84.       || buf == NULL
  85. #endif
  86.        )    {
  87.         errno = EINVAL;        /* invalid argument */
  88.         return NULL;
  89.         }
  90.  
  91.     buffer = buf;
  92. #ifdef XPG2
  93.     if ( buf == NULL        /* wants us to malloc() the string */
  94.       && (buffer = (char *) malloc( (unsigned) size )) == NULL
  95.     /* XXX -- actually should probably not pay attention to "size" arg */
  96.        )    {
  97.         errno = ENOMEM;        /* cannot malloc() specified size */
  98.         return NULL;
  99.         }
  100. #endif
  101.  
  102.     if ( stat( ".", dd ) != 0 )    /* prime the pump */
  103.         goto error;        /* errno already set */
  104.  
  105.     endp = buffer;            /* initially, empty string */
  106.     bufend = &buffer[size];
  107.  
  108.     for ( dotdot = &dotdots[sizeof dotdots]; dotdot != dotdots; )
  109.         {
  110.         dotdot -= 3;        /* include one more "/.." section */
  111.                     /* (first time is actually "..") */
  112.  
  113.         /* swap stat() info buffers */
  114.         {
  115.         register struct stat    *temp = d;
  116.  
  117.         d = dd;            /* new current dir is old parent dir */
  118.         dd = temp;
  119.         }
  120.  
  121.         if ( (dirp = opendir( dotdot )) == NULL )    /* new parent */
  122.             goto error;    /* errno already set */
  123.  
  124.         if ( fstat( dirp->dd_fd, dd ) != 0 )
  125.             {
  126.             serrno = errno;    /* set by fstat() */
  127.             (void)closedir( dirp );
  128.             errno = serrno;    /* in case closedir() clobbered it */
  129.             goto error;
  130.             }
  131.  
  132.         if ( d->st_dev == dd->st_dev )
  133.             {        /* not crossing a mount point */
  134.             if ( d->st_ino == dd->st_ino )
  135.                 {    /* root directory */
  136.                 dname = "";
  137.                 goto append;
  138.                 }
  139.  
  140.             do
  141.                 if ( (dir = readdir( dirp )) == NULL )
  142.                     {
  143.                     (void)closedir( dirp );
  144.                     errno = ENOENT;    /* missing entry */
  145.                     goto error;
  146.                     }
  147.             while ( dir->d_ino != d->st_ino );
  148.             }
  149.         else    {        /* crossing a mount point */
  150.             struct stat    t;    /* info re. test entry */
  151.             char        name[sizeof dotdots + 1 + NAME_MAX];
  152.  
  153.             (void)strcpy( name, dotdot );
  154.             dname = &name[strlen( name )];
  155.             *dname++ = '/';
  156.  
  157.             do    {
  158.                 if ( (dir = readdir( dirp )) == NULL )
  159.                     {
  160.                     (void)closedir( dirp );
  161.                     errno = ENOENT;    /* missing entry */
  162.                     goto error;
  163.                     }
  164.  
  165.                 (void)strcpy( dname, dir->d_name );
  166.                 /* must fit if NAME_MAX is not a lie */
  167.                 }
  168.             while ( stat( name, &t ) != 0
  169.                  || t.st_ino != d->st_ino
  170.                  || t.st_dev != d->st_dev
  171.                   );
  172.             }
  173.  
  174.         dname = dir->d_name;
  175.  
  176.         /* append "/" and reversed dname string onto buffer */
  177.     append:
  178.         if ( endp != buffer    /* avoid trailing / in final name */
  179.           || dname[0] == '\0'    /* but allow "/" when CWD is root */
  180.            )
  181.             *endp++ = '/';
  182.  
  183.         {
  184.         register char    *app;    /* traverses dname string */
  185.  
  186.         for ( app = dname; *app != '\0'; ++app )
  187.             ;
  188.  
  189.         if ( app - dname >= bufend - endp )
  190.             {
  191.             (void)closedir( dirp );
  192.             errno = ERANGE;    /* won't fit allotted space */
  193.             goto error;
  194.             }
  195.  
  196.         while ( app != dname )
  197.             *endp++ = *--app;
  198.         }
  199.  
  200.         (void)closedir( dirp );
  201.  
  202.         if ( dname[0] == '\0' )    /* reached root; wrap it up */
  203.             {
  204.             register char    *startp;    /* -> buffer[.] */
  205.  
  206.             *endp = '\0';    /* plant null terminator */
  207.  
  208.             /* straighten out reversed pathname string */
  209.             for ( startp = buffer; --endp > startp; ++startp )
  210.                 {
  211.                 char    temp = *endp;
  212.  
  213.                 *endp = *startp;
  214.                 *startp = temp;
  215.                 }
  216.  
  217.             errno = serrno;    /* restore entry errno */
  218.             /* XXX -- if buf==NULL, realloc here? */
  219.             return buffer;
  220.             }
  221.         }
  222.  
  223.     errno = ENOMEM;            /* actually, algorithm failure */
  224.  
  225.     error:
  226.     if ( buf == NULL )
  227.         free( (pointer)buffer );
  228.  
  229.     return NULL;
  230.     }
  231.  
  232.